﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Linq;

namespace FieldTools.Util
{
    public enum MD5Length
    {
        L16 = 16,
        L32 = 32
    }

    public class AESKey
    {
        /// <summary>
        /// ase key
        /// </summary>
        public string Key { get; set; }

        /// <summary>
        /// ase IV
        /// </summary>
        public string IV { get; set; }
    }
    public static partial class Extension
    {
        #region AES

        /*
        AES:16位密钥=128位，24位密钥=192位，32位密钥=256位,IV均为16位
        */

        /// <summary>
        /// Create ase key
        /// </summary>
        /// <returns></returns>
        public static AESKey CreateAesKey()
        {
            return new AESKey()
            {
                Key = RandomHelper.RandomString(32),
                IV = RandomHelper.RandomString(16)
            };
        }

        /// <summary>  
        /// AES encrypt
        /// </summary>  
        /// <param name="data">Raw data</param>  
        /// <param name="key">Key, requires 32 bits</param>  
        /// <param name="vector">IV,requires 16 bits</param>  
        /// <returns>Encrypted string</returns>  
        public static string AESEncrypt(this string data, string key, string vector)
        {
            byte[] plainBytes = Encoding.UTF8.GetBytes(data);

            var encryptBytes = AESEncrypt(plainBytes, key, vector);
            if (encryptBytes == null)
            {
                return null;
            }
            return Convert.ToBase64String(encryptBytes);
        }
        public static string AESEncryptPadWithZore(this string data, string key, string vector)
        {
            //byte[] plainBytes = Encoding.UTF8.GetBytes(data);
            //int length = 16 - (plainBytes.Length % 16);
            ////长度不满足16的倍数用"\0"右填充到16的倍数
            //StringBuilder add = new();
            //for (int j = 0; j < length; j++)
            //{
            //    add.Append("\0");
            //}
            ////补充 "\0"
            //data += add.ToString();

            return AesEncryptHuaShi(data, key, vector);
        }
        public static byte[] StrToToHexByte(this string hexString)
        {
            hexString = hexString.Replace(" ", "");
            if ((hexString.Length % 2) != 0)
                hexString += " ";
            byte[] returnBytes = new byte[hexString.Length / 2];
            for (int i = 0; i < returnBytes.Length; i++)
                returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
            return returnBytes;
        }
        /// <summary>
        /// 字节数组转16进制字符串
        /// </summary>
        /// <param name="bytes"></param>
        /// <returns></returns>
        public static string ByteToHexStr(this byte[] bytes)
        {
            string returnStr = "";
            if (bytes != null)
            {
                for (int i = 0; i < bytes.Length; i++)
                {
                    returnStr += bytes[i].ToString("x2");
                }
            }
            return returnStr;
        }

        public static string AesEncryptHuaShi(this string content, string aesKey, string aesIV)
        {
            if (string.IsNullOrWhiteSpace(content))
            {
                return null;
            }

            byte[] byteKEY = Encoding.UTF8.GetBytes(aesKey);
            byte[] byteIV = StrToToHexByte(aesIV);
            byte[] byteToEncryptArray = Encoding.UTF8.GetBytes(content);
            List<byte> byteToEncryptList = byteToEncryptArray.ToList();
            int length = 16 - (byteToEncryptArray.Length % 16);
            byte zero = 0;
            for (int j = 0; j < length; j++)
            {
                byteToEncryptList.Add(zero);
            }
            byteToEncryptArray = byteToEncryptList.ToArray();
            try
            {
                RijndaelManaged rDel = new RijndaelManaged
                {
                    Key = byteKEY,
                    IV = byteIV,
                    Mode = CipherMode.CBC,
                    Padding = PaddingMode.None,
                    BlockSize = 128
                };

                ICryptoTransform cTransform = rDel.CreateEncryptor();

                byte[] resultArray = cTransform.TransformFinalBlock(byteToEncryptArray, 0, byteToEncryptArray.Length);
                return ByteToHexStr(resultArray);
            }
            catch (Exception ex)
            {
                return "";
            }
        }
        public static string AesDecryptHuaShi(this string content, string aesKey, string aesIV)
        {
            if (string.IsNullOrWhiteSpace(content))
            {
                return null;
            }

            byte[] byteKEY = Encoding.UTF8.GetBytes(aesKey);
            byte[] byteIV = StrToToHexByte(aesIV);
            byte[] byteToEncryptArray = StrToToHexByte(content);

            using (Aes Aes = Aes.Create())
            {
                try
                {
                    using MemoryStream Memory = new(byteToEncryptArray);
                    using CryptoStream Decryptor = new(Memory, Aes.CreateDecryptor(byteKEY, byteIV), CryptoStreamMode.Read);
                    using MemoryStream tempMemory = new();
                    byte[] Buffer = new byte[1024];
                    Int32 readBytes = 0;
                    while ((readBytes = Decryptor.Read(Buffer, 0, Buffer.Length)) > 0)
                    {
                        tempMemory.Write(Buffer, 0, readBytes);
                    }

                    var decryptedData = tempMemory.ToArray();
                    return Encoding.UTF8.GetString(decryptedData);
                }
                catch
                {
                    return null;
                }
            }
        }

        /// <summary>
        /// AES encrypt
        /// </summary>
        /// <param name="data">Raw data</param>  
        /// <param name="key">Key, requires 32 bits</param>  
        /// <param name="vector">IV,requires 16 bits</param>  
        /// <returns>Encrypted byte array</returns>  
        public static byte[] AESEncrypt(this byte[] data, string key, string vector)
        {
            data.IsNotEmpty(nameof(data));

            key.IsNotEmpty(nameof(key));
            key.Length.IsNotOutOfRange(32, 32, nameof(key));

            vector.IsNotEmpty(nameof(vector));
            vector.Length.IsNotOutOfRange(16, 16, nameof(vector));


            byte[] plainBytes = data;
            byte[] bKey = new byte[32];
            Array.Copy(Encoding.UTF8.GetBytes(key.PadRight(bKey.Length)), bKey, bKey.Length);
            byte[] bVector = new byte[16];
            Array.Copy(Encoding.UTF8.GetBytes(vector.PadRight(bVector.Length)), bVector, bVector.Length);

            byte[] encryptData = null; // encrypted data
            using Aes Aes = Aes.Create();
            try
            {
                using MemoryStream Memory = new();
                using CryptoStream Encryptor = new(Memory,
                 Aes.CreateEncryptor(bKey, bVector),
                 CryptoStreamMode.Write);
                Encryptor.Write(plainBytes, 0, plainBytes.Length);
                Encryptor.FlushFinalBlock();

                encryptData = Memory.ToArray();
            }
            catch
            {
                encryptData = null;
            }
            return encryptData;
        }

        /// <summary>  
        ///  AES decrypt
        /// </summary>  
        /// <param name="data">Encrypted data</param>  
        /// <param name="key">Key, requires 32 bits</param>  
        /// <param name="vector">IV,requires 16 bits</param>  
        /// <returns>Decrypted string</returns>  
        public static string AESDecrypt(this string data, string key, string vector)
        {
            data.IsNotEmpty(nameof(data));

            key.IsNotEmpty(nameof(key));
            key.Length.IsNotOutOfRange(32, 32, nameof(key));

            vector.IsNotEmpty(nameof(vector));
            vector.Length.IsNotOutOfRange(16, 16, nameof(vector));


            byte[] encryptedBytes = Convert.FromBase64String(data);

            byte[] decryptBytes = AESDecrypt(encryptedBytes, key, vector);

            if (decryptBytes == null)
            {
                return null;
            }
            return Encoding.UTF8.GetString(decryptBytes);
        }

        /// <summary>  
        ///  AES decrypt
        /// </summary>  
        /// <param name="data">Encrypted data</param>  
        /// <param name="key">Key, requires 32 bits</param>  
        /// <param name="vector">IV,requires 16 bits</param>  
        /// <returns>Decrypted byte array</returns>  

        public static byte[] AESDecrypt(this byte[] data, string key, string vector)
        {
            data.IsNotEmpty(nameof(data));

            key.IsNotEmpty(nameof(key));
            key.Length.IsNotOutOfRange(32, 32, nameof(key));

            vector.IsNotEmpty(nameof(vector));
            vector.Length.IsNotOutOfRange(16, 16, nameof(vector));


            byte[] encryptedBytes = data;
            byte[] bKey = new byte[32];
            Array.Copy(Encoding.UTF8.GetBytes(key.PadRight(bKey.Length)), bKey, bKey.Length);
            byte[] bVector = new byte[16];
            Array.Copy(Encoding.UTF8.GetBytes(vector.PadRight(bVector.Length)), bVector, bVector.Length);

            byte[] decryptedData = null; // decrypted data

            using (Aes Aes = Aes.Create())
            {
                try
                {
                    using (MemoryStream Memory = new MemoryStream(encryptedBytes))
                    {
                        using (CryptoStream Decryptor = new CryptoStream(Memory, Aes.CreateDecryptor(bKey, bVector), CryptoStreamMode.Read))
                        {
                            using (MemoryStream tempMemory = new MemoryStream())
                            {
                                byte[] Buffer = new byte[1024];
                                Int32 readBytes = 0;
                                while ((readBytes = Decryptor.Read(Buffer, 0, Buffer.Length)) > 0)
                                {
                                    tempMemory.Write(Buffer, 0, readBytes);
                                }

                                decryptedData = tempMemory.ToArray();
                            }
                        }
                    }
                }
                catch
                {
                    decryptedData = null;
                }

                return decryptedData;
            }
        }

        /// <summary>  
        /// AES encrypt ( no IV)  
        /// </summary>  
        /// <param name="data">Raw data</param>  
        /// <param name="key">Key, requires 32 bits</param>  
        /// <returns>Encrypted string</returns>  
        public static string AESEncrypt(this string data, string key)
        {
            data.IsNotEmpty(nameof(data));

            key.IsNotEmpty(nameof(key));
            key.Length.IsNotOutOfRange(32, 32, nameof(key));

            using (MemoryStream Memory = new())
            {
                using (Aes aes = Aes.Create())
                {
                    byte[] plainBytes = Encoding.UTF8.GetBytes(data);
                    byte[] bKey = new byte[32];
                    Array.Copy(Encoding.UTF8.GetBytes(key.PadRight(bKey.Length)), bKey, bKey.Length);

                    aes.Mode = CipherMode.ECB;
                    aes.Padding = PaddingMode.PKCS7;
                    aes.KeySize = 256;
                    aes.Key = bKey;

                    using (CryptoStream cryptoStream = new CryptoStream(Memory, aes.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        try
                        {
                            cryptoStream.Write(plainBytes, 0, plainBytes.Length);
                            cryptoStream.FlushFinalBlock();
                            return Convert.ToBase64String(Memory.ToArray());
                        }
                        catch (Exception ex)
                        {
                            return null;
                        }
                    }
                }
            }
        }

        /// <summary>  
        /// AES decrypt( no IV)  
        /// </summary>  
        /// <param name="data">Encrypted data</param>  
        /// <param name="key">Key, requires 32 bits</param>  
        /// <returns>Decrypted string</returns>  
        public static string AESDecrypt(this string data, string key)
        {
            data.IsNotEmpty(nameof(data));

            key.IsNotEmpty(nameof(key));
            key.Length.IsNotOutOfRange(32, 32, nameof(key));


            byte[] encryptedBytes = Convert.FromBase64String(data);
            byte[] bKey = new byte[32];
            Array.Copy(Encoding.UTF8.GetBytes(key.PadRight(bKey.Length)), bKey, bKey.Length);

            using (MemoryStream Memory = new MemoryStream(encryptedBytes))
            {
                using (Aes aes = Aes.Create())
                {
                    aes.Mode = CipherMode.ECB;
                    aes.Padding = PaddingMode.PKCS7;
                    aes.KeySize = 256;
                    aes.Key = bKey;

                    using (CryptoStream cryptoStream = new CryptoStream(Memory, aes.CreateDecryptor(), CryptoStreamMode.Read))
                    {
                        try
                        {
                            byte[] tmp = new byte[encryptedBytes.Length];
                            int len = cryptoStream.Read(tmp, 0, encryptedBytes.Length);
                            byte[] ret = new byte[len];
                            Array.Copy(tmp, 0, ret, 0, len);

                            return Encoding.UTF8.GetString(ret, 0, len);
                        }
                        catch (Exception ex)
                        {
                            return null;
                        }
                    }
                }
            }
        }
        /// <summary>
        ///  AES 加密
        /// </summary>
        /// <param name="toEncrypt">明文（待加密）</param>
        /// <param name="hexKey">密钥（确保java提供给你的是16进制密钥，不是十进制！）</param>
        /// <returns>AES加密结果</returns>
        public static string AesEncrypt(this string toEncrypt, string hexKey)
        {
            if (toEncrypt.IsNullOrEmpty())
            {
                return "";
            }
            //将16进制秘钥转成字节数组
            var keyArray = new byte[hexKey.Length / 2];
            for (var x = 0; x < keyArray.Length; x++)
            {
                var i = Convert.ToInt32(hexKey.Substring(x * 2, 2), 16);
                keyArray[x] = (byte)i;
            }

            byte[] toEncryptArray = Encoding.UTF8.GetBytes(toEncrypt);
            RijndaelManaged rDel = new RijndaelManaged();
            rDel.Key = keyArray;
            rDel.Mode = CipherMode.ECB;
            rDel.Padding = PaddingMode.PKCS7;

            ICryptoTransform cTransform = rDel.CreateEncryptor();
            byte[] resultArray = cTransform.TransformFinalBlock(toEncryptArray, 0, toEncryptArray.Length);
            return resultArray.ToHexValueString();
        }

        /// <summary>
        /// 用种子获取密钥字节
        /// </summary>
        /// <param name="strKey">密钥种子</param>
        /// <param name="encoding">编码格式</param>
        /// <param name="nLen">密钥长度（一般为16，不清楚时不要随意动）</param>
        /// <returns></returns>
        public static byte[] GetKeyBySeed(string strKey, Encoding encoding, int nLen = 16)
        {
            byte[] bySeed = encoding.GetBytes(strKey);
            byte[] byKeyArray = null;
            using (var st = new SHA1CryptoServiceProvider())
            {
                using (var nd = new SHA1CryptoServiceProvider())
                {
                    var rd = nd.ComputeHash(st.ComputeHash(bySeed));
                    byKeyArray = rd.Take(nLen).ToArray();
                }
            }
            return byKeyArray;
        }

        /// <summary>
        ///  加密 参数：string
        /// </summary>
        /// <param name="strCon">加密内容</param>
        /// <param name="byteKey">密钥字节数组</param>
        /// <param name="strIv">向量(注意目前只研究支持16位长度)</param>
        /// <param name="encoding">编码方式</param>
        /// <returns>string：密文</returns>
        public static string AesCBCEncrypt(this string strCon, string strKey, string strIv, Encoding encoding)
        {
            try
            {
                if (string.IsNullOrWhiteSpace(strCon))
                {
                    return null;
                }

                byte[] byteKey = GetKeyBySeed(strKey, encoding);
                byte[] byCon = encoding.GetBytes(strCon);
                var rm = new RijndaelManaged
                {
                    IV = encoding.GetBytes(strIv),
                    Key = byteKey,
                    Mode = CipherMode.CBC,
                    Padding = PaddingMode.PKCS7
                };
                ICryptoTransform cTransform = rm.CreateEncryptor();
                byte[] resultArray = cTransform.TransformFinalBlock(byCon, 0, byCon.Length);
                return Convert.ToBase64String(resultArray, 0, resultArray.Length);
            }
            catch
            {
                return "";
            }
        }

        #endregion

        #region DES

        /// <summary>
        /// Create des key
        /// </summary>
        /// <returns></returns>
        public static string CreateDesKey()
        {
            return RandomHelper.RandomString(24);
        }

        /// <summary>
        /// Create des iv
        /// </summary>
        /// <returns></returns>
        public static string CreateDesIv()
        {
            return RandomHelper.RandomString(8);
        }

        /// <summary>  
        /// DES encrypt
        /// </summary>  
        /// <param name="data">Raw data</param>  
        /// <param name="key">Key, requires 24 bits</param>  
        /// <returns>Encrypted string</returns>  
        public static string DESEncrypt(this string data, string key)
        {
            data.IsNotEmpty(nameof(data));
            key.IsNotEmpty(nameof(key));
            key.Length.IsNotOutOfRange(24, 24, nameof(key));

            byte[] plainBytes = Encoding.UTF8.GetBytes(data);
            var encryptBytes = DESEncrypt(plainBytes, key, CipherMode.ECB);

            if (encryptBytes == null)
            {
                return null;
            }
            return Convert.ToBase64String(encryptBytes);
        }

        /// <summary>  
        /// DES encrypt
        /// </summary>  
        /// <param name="data">Raw data byte array</param>  
        /// <param name="key">Key, requires 24 bits</param>  
        /// <returns>Encrypted byte array</returns>  
        public static byte[] DESEncrypt(this byte[] data, string key)
        {
            data.IsNotEmpty(nameof(data));
            key.IsNotEmpty(nameof(key));
            key.Length.IsNotOutOfRange(24, 24, nameof(key));

            return DESEncrypt(data, key, CipherMode.ECB);
        }


        /// <summary>  
        /// DES encrypt
        /// </summary>  
        /// <param name="data">Raw data byte array</param>  
        /// <param name="key">Key, requires 24 bits</param>  
        /// <param name="vector">IV,requires 8 bits</param>  
        /// <returns>Encrypted byte array</returns>  
        public static byte[] DESEncrypt(this byte[] data, string key, string vector)
        {
            data.IsNotEmpty(nameof(data));
            key.IsNotEmpty(nameof(key));
            key.Length.IsNotOutOfRange(24, 24, nameof(key));
            vector.IsNotEmpty(nameof(vector));
            vector.Length.IsNotOutOfRange(8, 8, nameof(vector));

            return DESEncrypt(data, key, CipherMode.CBC, vector);
        }

        /// <summary>  
        /// DES encrypt
        /// </summary>  
        /// <param name="data">Raw data</param>  
        /// <param name="key">Key, requires 24 bits</param>  
        /// <param name="cipherMode"><see cref="CipherMode"/></param>  
        /// <param name="paddingMode"><see cref="PaddingMode"/> default is PKCS7</param>  
        /// <param name="vector">IV,requires 8 bits</param>  
        /// <returns>Encrypted byte array</returns>  
        private static byte[] DESEncrypt(this byte[] data, string key, CipherMode cipherMode, string vector = "", PaddingMode paddingMode = PaddingMode.PKCS7)
        {
            data.IsNotEmpty(nameof(data));
            key.IsNotEmpty(nameof(key));
            key.Length.IsNotOutOfRange(24, 24, nameof(key));

            using (MemoryStream Memory = new MemoryStream())
            {
                using (TripleDES des = TripleDES.Create())
                {
                    byte[] plainBytes = data;
                    byte[] bKey = new byte[24];
                    Array.Copy(Encoding.UTF8.GetBytes(key.PadRight(bKey.Length)), bKey, bKey.Length);

                    des.Mode = cipherMode;
                    des.Padding = paddingMode;
                    des.Key = bKey;

                    if (cipherMode == CipherMode.CBC)
                    {
                        byte[] bVector = new byte[8];
                        Array.Copy(Encoding.UTF8.GetBytes(vector.PadRight(bVector.Length)), bVector, bVector.Length);
                        des.IV = bVector;
                    }

                    using (CryptoStream cryptoStream = new CryptoStream(Memory, des.CreateEncryptor(), CryptoStreamMode.Write))
                    {
                        try
                        {
                            cryptoStream.Write(plainBytes, 0, plainBytes.Length);
                            cryptoStream.FlushFinalBlock();
                            return Memory.ToArray();
                        }
                        catch (Exception ex)
                        {
                            return null;
                        }
                    }
                }
            }
        }

        /// <summary>  
        /// DES decrypt
        /// </summary>  
        /// <param name="data">Encrypted data</param>  
        /// <param name="key">Key, requires 24 bits</param>  
        /// <returns>Decrypted string</returns>  
        public static string DESDecrypt(this string data, string key)
        {
            data.IsNotEmpty(nameof(data));
            key.IsNotEmpty(nameof(key));
            key.Length.IsNotOutOfRange(24, 24, nameof(key));

            byte[] encryptedBytes = Convert.FromBase64String(data);
            byte[] bytes = DESDecrypt(encryptedBytes, key, CipherMode.ECB);

            if (bytes == null)
            {
                return null;
            }
            return Encoding.UTF8.GetString(bytes);
        }

        /// <summary>  
        /// DES decrypt
        /// </summary>  
        /// <param name="data">Encrypted data byte array</param>  
        /// <param name="key">Key, requires 24 bits</param>  
        /// <returns>Decrypted string</returns>  
        public static byte[] DESDecrypt(this byte[] data, string key)
        {
            data.IsNotEmpty(nameof(data));
            key.IsNotEmpty(nameof(key));
            key.Length.IsNotOutOfRange(24, 24, nameof(key));

            return DESDecrypt(data, key, CipherMode.ECB);
        }

        /// <summary>  
        /// DES encrypt
        /// </summary>  
        /// <param name="data">Raw data byte array</param>  
        /// <param name="key">Key, requires 24 bits</param>  
        /// <param name="vector">IV,requires 8 bits</param>  
        /// <returns>Encrypted byte array</returns>  
        public static byte[] DESDecrypt(this byte[] data, string key, string vector)
        {
            data.IsNotEmpty(nameof(data));
            key.IsNotEmpty(nameof(key));
            key.Length.IsNotOutOfRange(24, 24, nameof(key));
            vector.IsNotEmpty(nameof(vector));
            vector.Length.IsNotOutOfRange(8, 8, nameof(vector));

            return DESDecrypt(data, key, CipherMode.CBC, vector);
        }

        /// <summary>  
        /// DES decrypt
        /// </summary>  
        /// <param name="data">Encrypted data</param>  
        /// <param name="key">Key, requires 24 bits</param>  
        /// <param name="cipherMode"><see cref="CipherMode"/></param>  
        /// <param name="paddingMode"><see cref="PaddingMode"/> default is PKCS7</param>  
        /// <returns>Decrypted byte array</returns>  
        private static byte[] DESDecrypt(this byte[] data, string key, CipherMode cipherMode, string vector = "", PaddingMode paddingMode = PaddingMode.PKCS7)
        {
            data.IsNotEmpty(nameof(data));
            key.IsNotEmpty(nameof(key));
            key.Length.IsNotOutOfRange(24, 24, nameof(key));

            byte[] encryptedBytes = data;
            byte[] bKey = new byte[24];
            Array.Copy(Encoding.UTF8.GetBytes(key.PadRight(bKey.Length)), bKey, bKey.Length);

            using (MemoryStream Memory = new MemoryStream(encryptedBytes))
            {
                using (TripleDES des = TripleDES.Create())
                {
                    des.Mode = cipherMode;
                    des.Padding = paddingMode;
                    des.Key = bKey;

                    if (cipherMode == CipherMode.CBC)
                    {
                        byte[] bVector = new byte[8];
                        Array.Copy(Encoding.UTF8.GetBytes(vector.PadRight(bVector.Length)), bVector, bVector.Length);
                        des.IV = bVector;
                    }

                    using (CryptoStream cryptoStream = new CryptoStream(Memory, des.CreateDecryptor(), CryptoStreamMode.Read))
                    {
                        try
                        {
                            byte[] tmp = new byte[encryptedBytes.Length];
                            int len = cryptoStream.Read(tmp, 0, encryptedBytes.Length);
                            byte[] ret = new byte[len];
                            Array.Copy(tmp, 0, ret, 0, len);
                            return ret;
                        }
                        catch
                        {
                            return null;
                        }
                    }
                }
            }
        }

        #endregion

        #region 3DES

        public static string TripleDESEncrypt(this string data, string key, string desIv)
        {
            byte[] keyArray = GetKey(Encoding.UTF8.GetBytes(key));
            byte[] toEncryptArray = Encoding.UTF8.GetBytes(data);
            byte[] databytes;
            if (!desIv.IsNullOrEmpty())
            {
                var desIV = Encoding.UTF8.GetBytes(desIv);
                databytes = DESEncrypt(toEncryptArray, keyArray, desIV);
            }
            else
            {
                databytes = DESEncrypt(toEncryptArray, keyArray);
            }
            return Convert.ToBase64String(databytes, 0, data.Length);
        }
        private static byte[] GetKey(byte[] keyBytes, int baseLen = 24)
        {
            if ((keyBytes.Length % baseLen) != 0)
            {
                int groups = (keyBytes.Length / baseLen) + ((keyBytes.Length % baseLen) != 0 ? 1 : 0);
                byte[] temp = new byte[groups * baseLen];
                // 关键代码 补全
                BitConverter.ToInt32(temp, 0);
                Array.Copy(keyBytes, 0, temp, 0, keyBytes.Length);
                keyBytes = temp;
            }
            return keyBytes;
        }
        private static byte[] DESEncrypt(byte[] plain, byte[] desKey, byte[] desIV = null)
        {
            int offset = 0;
            int count = plain.Length;
            TripleDESCryptoServiceProvider desProvier = new TripleDESCryptoServiceProvider();
            MemoryStream msEncrypt = new MemoryStream();
            CryptoStream csEncrypt = new CryptoStream(msEncrypt, desProvier.CreateEncryptor(desKey, desIV), CryptoStreamMode.Write);
            csEncrypt.Write(plain, offset, count);
            csEncrypt.Close();
            byte[] bytesCipher = msEncrypt.ToArray();
            msEncrypt.Close();
            return bytesCipher;
        }

        private static byte[] DESDecrypt(byte[] cipher, byte[] desKey, byte[] desIV = null)
        {
            TripleDESCryptoServiceProvider desProvier = new TripleDESCryptoServiceProvider();
            MemoryStream msDecrypt = new MemoryStream(cipher);
            CryptoStream csDecrypt = new CryptoStream(msDecrypt, desProvier.CreateDecryptor(desKey, desIV), CryptoStreamMode.Read);

            MemoryStream msPlain = new MemoryStream();
            int b;
            while ((b = csDecrypt.ReadByte()) != -1)
            {
                msPlain.WriteByte((byte)b);
            }
            byte[] bytesPlain = msPlain.ToArray();
            msPlain.Close();
            csDecrypt.Close();
            msDecrypt.Close();
            return bytesPlain;
        }

        #endregion

        #region MD5

        /// <summary>
        /// 转换为MD5加密后的字符串（默认加密为32位）
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string ToMD5String(this string str)
        {
            if (str.IsNullOrEmpty())
            {
                return "";
            }
            MD5 md5 = MD5.Create();
            byte[] inputBytes = Encoding.UTF8.GetBytes(str);
            byte[] hashBytes = md5.ComputeHash(inputBytes);

            StringBuilder sb = new();
            for (int i = 0; i < hashBytes.Length; i++)
            {
                sb.Append(hashBytes[i].ToString("x2"));
            }
            md5.Dispose();

            return sb.ToString();
        }

        /// <summary>
        /// 转换为MD5加密后的字符串（16位）
        /// </summary>
        /// <param name="str"></param>
        /// <returns></returns>
        public static string ToMD5String16(this string str)
        {
            return str.ToMD5String().Substring(8, 16);
        }
        /// <summary>
        /// MD5 hash
        /// </summary>
        /// <param name="srcString">The string to be encrypted.</param>
        /// <param name="length">The length of hash result , default value is <see cref="MD5Length.L32"/>.</param>
        /// <returns></returns>
        public static string ToMD5(this string srcString, MD5Length length = MD5Length.L32)
        {
            if (srcString.IsNullOrEmpty())
            {
                return "";
            }
            string str_md5_out = string.Empty;
            using (MD5 md5 = MD5.Create())
            {
                byte[] bytes_md5_in = Encoding.UTF8.GetBytes(srcString);
                byte[] bytes_md5_out = md5.ComputeHash(bytes_md5_in);

                str_md5_out = length == MD5Length.L32
                    ? BitConverter.ToString(bytes_md5_out)
                    : BitConverter.ToString(bytes_md5_out, 4, 8);

                str_md5_out = str_md5_out.Replace("-", "");
                return str_md5_out;
            }
        }
        #endregion

        #region HMACMD5
        /// <summary>
        /// HMACMD5 hash
        /// </summary>
        /// <param name="srcString">The string to be encrypted</param>
        /// <param name="key">encrypte key</param>
        /// <returns></returns>
        public static string HMACMD5(this string srcString, string key)
        {
            srcString.IsNotEmpty(nameof(srcString));
            key.IsNotEmpty(nameof(key));

            byte[] secrectKey = Encoding.UTF8.GetBytes(key);
            using HMACMD5 md5 = new(secrectKey);
            byte[] bytes_md5_in = Encoding.UTF8.GetBytes(srcString);
            byte[] bytes_md5_out = md5.ComputeHash(bytes_md5_in);
            string str_md5_out = BitConverter.ToString(bytes_md5_out);
            str_md5_out = str_md5_out.Replace("-", "");
            return str_md5_out;
        }

        #endregion

        #region Base64


        /// <summary>
        /// Base64加密
        /// 注:默认采用UTF8编码
        /// </summary>
        /// <param name="source">待加密的明文</param>
        /// <returns>加密后的字符串</returns>
        public static string Base64Encode(this string source)
        {
            return Base64Encode(source, Encoding.UTF8);
        }

        /// <summary>
        /// Base64加密
        /// </summary>
        /// <param name="source">待加密的明文</param>
        /// <param name="encoding">加密采用的编码方式</param>
        /// <returns></returns>
        public static string Base64Encode(this string source, Encoding encoding)
        {
            string encode = string.Empty;
            byte[] bytes = encoding.GetBytes(source);
            try
            {
                encode = Convert.ToBase64String(bytes);
            }
            catch
            {
                encode = source;
            }
            return encode;
        }

        /// <summary>
        /// Base64解密
        /// 注:默认使用UTF8编码
        /// </summary>
        /// <param name="result">待解密的密文</param>
        /// <returns>解密后的字符串</returns>
        public static string Base64Decode(this string result)
        {
            return Base64Decode(result, Encoding.UTF8);
        }

        /// <summary>
        /// Base64解密
        /// </summary>
        /// <param name="result">待解密的密文</param>
        /// <param name="encoding">解密采用的编码方式，注意和加密时采用的方式一致</param>
        /// <returns>解密后的字符串</returns>
        public static string Base64Decode(this string result, Encoding encoding)
        {
            string decode = string.Empty;
            byte[] bytes = Convert.FromBase64String(result);
            try
            {
                decode = encoding.GetString(bytes);
            }
            catch
            {
                decode = result;
            }
            return decode;
        }

        /// <summary>
        /// Base64Url编码
        /// </summary>
        /// <param name="text">待编码的文本字符串</param>
        /// <returns>编码的文本字符串</returns>
        public static string Base64UrlEncode(this string text)
        {
            var plainTextBytes = Encoding.UTF8.GetBytes(text);
            var base64 = Convert.ToBase64String(plainTextBytes).Replace('+', '-').Replace('/', '_').TrimEnd('=');

            return base64;
        }

        /// <summary>
        /// Base64Url解码
        /// </summary>
        /// <param name="base64UrlStr">使用Base64Url编码后的字符串</param>
        /// <returns>解码后的内容</returns>
        public static string Base64UrlDecode(this string base64UrlStr)
        {
            base64UrlStr = base64UrlStr.Replace('-', '+').Replace('_', '/');
            switch (base64UrlStr.Length % 4)
            {
                case 2:
                    base64UrlStr += "==";
                    break;
                case 3:
                    base64UrlStr += "=";
                    break;
            }
            var bytes = Convert.FromBase64String(base64UrlStr);

            return Encoding.UTF8.GetString(bytes);
        }

        #endregion

        #region SHA1


        /// <summary>
        /// 计算SHA1摘要
        /// 注：默认使用UTF8编码
        /// </summary>
        /// <param name="str">字符串</param>
        /// <returns></returns>
        public static byte[] ToSHA1Bytes(this string str)
        {
            return str.ToSHA1Bytes(Encoding.UTF8);
        }

        /// <summary>
        /// 计算SHA1摘要
        /// </summary>
        /// <param name="str">字符串</param>
        /// <param name="encoding">编码</param>
        /// <returns></returns>
        public static byte[] ToSHA1Bytes(this string str, Encoding encoding)
        {
            SHA1 sha1 = new SHA1CryptoServiceProvider();
            byte[] inputBytes = encoding.GetBytes(str);
            byte[] outputBytes = sha1.ComputeHash(inputBytes);

            return outputBytes;
        }

        /// <summary>
        /// 转为SHA1哈希加密字符串
        /// 注：默认使用UTF8编码
        /// </summary>
        /// <param name="str">字符串</param>
        /// <returns></returns>
        public static string ToSHA1String(this string str)
        {
            if (str.IsNullOrEmpty())
            {
                return "";
            }
            return str.ToSHA1String(Encoding.UTF8);
        }

        /// <summary>
        /// 转为SHA1哈希
        /// </summary>
        /// <param name="str">字符串</param>
        /// <param name="encoding">编码</param>
        /// <returns></returns>
        public static string ToSHA1String(this string str, Encoding encoding)
        {
            byte[] sha1Bytes = str.ToSHA1Bytes(encoding);
            string resStr = BitConverter.ToString(sha1Bytes);
            return resStr.Replace("-", "").ToLower();
        }

        #endregion

        #region SHA256


        /// <summary>
        /// SHA256加密
        /// </summary>
        /// <param name="str">字符串</param>
        /// <returns></returns>
        public static string ToSHA256String(this string str)
        {
            byte[] bytes = Encoding.UTF8.GetBytes(str);
            byte[] hash = SHA256.Create().ComputeHash(bytes);

            StringBuilder builder = new StringBuilder();
            for (int i = 0; i < hash.Length; i++)
            {
                builder.Append(hash[i].ToString("x2"));
            }

            return builder.ToString();
        }

        /// <summary>
        /// HMACSHA256算法
        /// </summary>
        /// <param name="text">内容</param>
        /// <param name="secret">密钥</param>
        /// <returns></returns>
        public static string ToHMACSHA256String(this string text, string secret)
        {
            secret = secret ?? "";
            byte[] keyByte = Encoding.UTF8.GetBytes(secret);
            byte[] messageBytes = Encoding.UTF8.GetBytes(text);
            using var hmacsha256 = new HMACSHA256(keyByte);
            byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
            return Convert.ToBase64String(hashmessage).Replace('+', '-').Replace('/', '_').TrimEnd('=');
        }

        #endregion

        #region SHA384

        /// <summary>
        /// SHA384 encrypt
        /// </summary>
        /// <param name="srcString">The string to be encrypted</param>
        /// <returns></returns>
        public static string Sha384(this string srcString)
        {
            srcString.IsNotEmpty(nameof(srcString));
            using SHA384 sha384 = SHA384.Create();
            byte[] bytes_sha384_in = Encoding.UTF8.GetBytes(srcString);
            byte[] bytes_sha384_out = sha384.ComputeHash(bytes_sha384_in);
            string str_sha384_out = BitConverter.ToString(bytes_sha384_out);
            str_sha384_out = str_sha384_out.Replace("-", "");
            return str_sha384_out;

        }

        #endregion

        #region SHA512
        /// <summary>
        /// SHA512 encrypt
        /// </summary>
        /// <param name="srcString">The string to be encrypted</param>
        /// <returns></returns>
        public static string Sha512(this string srcString)
        {
            srcString.IsNotEmpty(nameof(srcString));
            using SHA512 sha512 = SHA512.Create();
            byte[] bytes_sha512_in = Encoding.UTF8.GetBytes(srcString);
            byte[] bytes_sha512_out = sha512.ComputeHash(bytes_sha512_in);
            string str_sha512_out = BitConverter.ToString(bytes_sha512_out);
            str_sha512_out = str_sha512_out.Replace("-", "");
            return str_sha512_out;
        }

        #endregion

        #region HMACSHA1

        /// <summary>
        /// HMAC_SHA1
        /// </summary>
        /// <param name="srcString">The string to be encrypted</param>
        /// <param name="key">encrypte key</param>
        /// <returns></returns>
        public static string HMACSHA1(this string srcString, string key)
        {
            srcString.IsNotEmpty(nameof(srcString));
            key.IsNotEmpty(nameof(key));

            byte[] secrectKey = Encoding.UTF8.GetBytes(key);
            using HMACSHA1 hmac = new(secrectKey);
            hmac.Initialize();

            byte[] bytes_hmac_in = Encoding.UTF8.GetBytes(srcString);
            byte[] bytes_hamc_out = hmac.ComputeHash(bytes_hmac_in);

            string str_hamc_out = BitConverter.ToString(bytes_hamc_out);
            str_hamc_out = str_hamc_out.Replace("-", "");

            return str_hamc_out;
        }

        #endregion

        #region HMACSHA256

        /// <summary>
        /// HMAC_SHA256 
        /// </summary>
        /// <param name="srcString">The string to be encrypted</param>
        /// <param name="key">encrypte key</param>
        /// <returns></returns>
        public static string HMACSHA256(this string srcString, string key)
        {
            srcString.IsNotEmpty(nameof(srcString));
            key.IsNotEmpty(nameof(key));

            byte[] secrectKey = Encoding.UTF8.GetBytes(key);
            using (HMACSHA256 hmac = new(secrectKey))
            {
                hmac.Initialize();

                byte[] bytes_hmac_in = Encoding.UTF8.GetBytes(srcString);
                byte[] bytes_hamc_out = hmac.ComputeHash(bytes_hmac_in);

                string str_hamc_out = BitConverter.ToString(bytes_hamc_out);
                str_hamc_out = str_hamc_out.Replace("-", "");

                return str_hamc_out;
            }
        }

        #endregion

        #region HMACSHA384

        /// <summary>
        /// HMAC_SHA384
        /// </summary>
        /// <param name="srcString">The string to be encrypted</param>
        /// <param name="key">encrypte key</param>
        /// <returns></returns>
        public static string HMACSHA384(this string srcString, string key)
        {
            srcString.IsNotEmpty(nameof(srcString));
            key.IsNotEmpty(nameof(key));

            byte[] secrectKey = Encoding.UTF8.GetBytes(key);
            using HMACSHA384 hmac = new(secrectKey);
            hmac.Initialize();

            byte[] bytes_hmac_in = Encoding.UTF8.GetBytes(srcString);
            byte[] bytes_hamc_out = hmac.ComputeHash(bytes_hmac_in);


            string str_hamc_out = BitConverter.ToString(bytes_hamc_out);
            str_hamc_out = str_hamc_out.Replace("-", "");

            return str_hamc_out;
        }

        #endregion

        #region HMACSHA512

        /// <summary>
        /// HMAC_SHA512
        /// </summary>
        /// <param name="srcString">The string to be encrypted</param>
        /// <param name="key">encrypte key</param>
        /// <returns></returns>
        public static string HMACSHA512(this string srcString, string key)
        {
            srcString.IsNotEmpty(nameof(srcString));
            key.IsNotEmpty(nameof(key));

            byte[] secrectKey = Encoding.UTF8.GetBytes(key);
            using HMACSHA512 hmac = new(secrectKey);
            hmac.Initialize();

            byte[] bytes_hmac_in = Encoding.UTF8.GetBytes(srcString);
            byte[] bytes_hamc_out = hmac.ComputeHash(bytes_hmac_in);

            string str_hamc_out = BitConverter.ToString(bytes_hamc_out);
            str_hamc_out = str_hamc_out.Replace("-", "");

            return str_hamc_out;
        }

        #endregion

        #region Machine Key

        /// <summary>
        /// Create decryptionKey
        /// </summary>
        /// <param name="length">decryption key length range is 16 -48</param>
        /// <returns>DecryptionKey</returns>
        public static string CreateDecryptionKey(this int length)
        {
            length.IsNotOutOfRange(16, 48, nameof(length));
            return CreateMachineKey(length);
        }

        /// <summary>
        /// Create validationKey
        /// </summary>
        /// <param name="length"></param>
        /// <returns>ValidationKey</returns>
        public static string CreateValidationKey(this int length)
        {
            length.IsNotOutOfRange(48, 128, nameof(length));
            return CreateMachineKey(length);
        }

        /// <summary>
        /// 使用加密服务提供程序实现加密生成随机数
        /// 
        /// 说明：
        /// validationKey 的值可以是48到128个字符长，强烈建议使用可用的最长密钥
        /// decryptionKey 的值可以是16到48字符长，建议使用48字符长
        /// 
        /// 使用方式：
        /// string decryptionKey = EncryptManager.CreateMachineKey(48);
        /// string validationKey = EncryptManager.CreateMachineKey(128);
        /// </summary>
        /// <param name="length">长度</param>
        /// <returns></returns>
        private static string CreateMachineKey(this int length)
        {

            byte[] random = new byte[length / 2];

            RandomNumberGenerator rng = RandomNumberGenerator.Create();
            rng.GetBytes(random);

            StringBuilder machineKey = new StringBuilder(length);
            for (int i = 0; i < random.Length; i++)
            {
                machineKey.Append(string.Format("{0:X2}", random[i]));
            }
            return machineKey.ToString();
        }

        #endregion
    }
}
